package com.juku2024.juku.viewmodel

import android.text.TextUtils
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.juku2024.juku.R
import com.juku2024.juku.db.NoteRoomUtils
import com.juku2024.juku.db.entity.VersionEntity
import com.juku2024.juku.network.api.Api
import com.juku2024.juku.network.retrofit.OnRetrofitResultListener
import com.juku2024.juku.network.retrofit.RetrofitUtils
import com.juku2024.juku.request.CheckAppRequest
import com.juku2024.juku.request.CommonRequest
import com.juku2024.juku.request.VersionRequest
import com.juku2024.juku.tools.*
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.juku2024.juku.model.*
import kotlinx.coroutines.*
import java.lang.Exception
import java.util.*

open class BaseViewModel : ViewModel() {

    /**
     * 获取UTC的时间戳
     */
    fun getSecondTimestamp(): Long {
        val zone = TimeZone.getTimeZone("UTC+8")
        val cal = Calendar.getInstance(zone)
        return cal.timeInMillis
    }

    fun getAppId(): String {
        return AppUtils.method03()
    }

    fun getAppKey(): String {
        return AppUtils.method04()
    }

    fun getExtra(): String {
        return AppUtils.method06()
    }


    /**
     * 获取version
     * 返回 version, isUpgrade
     */
    suspend fun getVersion(type: Int) = withContext(Dispatchers.IO) {
        try {
            val dao = NoteRoomUtils.getInstance().versionDao()
            var versionEntity = dao.query(type)
            if (versionEntity == null) {
                versionEntity = VersionEntity()
                versionEntity.type = type
                versionEntity.version = 1
                dao.insert(versionEntity)
            }
            val versionLong = versionEntity.version
            if (!NetworkUtils.isNetworkConnected()) {
                return@withContext VersionResponse(versionLong, false)
            }

            val appId = getAppId()
            val appKey = getAppKey()
            val timestamp = getSecondTimestamp()
            val sign = MD5Utils.getMD5String(type.toString(), versionEntity.version.toString(), appId, appKey, timestamp.toString(), getExtra())
            val request = VersionRequest(
                type.toString(),
                versionEntity.version.toString(),
                appId,
                timestamp.toString(),
                sign
            )
            val json = Gson().toJson(request)
            LogC.d("versionRequest -> $json")
            val commonRequest = CommonRequest()
            commonRequest.body = EncryptionUtils.aesEncrypt(json)
            commonRequest.ticket = EncryptionUtils.rsaEncrypt()
            val versionResponse = VersionResponse(versionLong, true)
            val result = RetrofitUtils.instance.getApi(Api::class.java).getVersion(commonRequest) ?: return@withContext versionResponse
            if (result.body == null || result.ticket == null) {
                versionResponse.isUpgrade = false
                return@withContext versionResponse
            }

            // 解密ticket
            val aseKey = EncryptionUtils.rsaDecrypt(result.ticket) ?: return@withContext versionResponse
            val bodyJson = EncryptionUtils.aesDecrypt(result.body, aseKey) ?: return@withContext versionResponse
            LogC.e("version -> $bodyJson")
            val version = Gson().fromJson(bodyJson, VersionResponse::class.java).apiVersion
            if (version == -1L) { // 不需要更新
                versionResponse.isUpgrade = false
                return@withContext versionResponse
            }
            val localVersion = versionEntity.version
            versionResponse.isUpgrade = version != localVersion
            if (versionResponse.isUpgrade) {
                versionEntity.version = version
                dao.insert(versionEntity)
                versionResponse.apiVersion = version
            }

            return@withContext versionResponse
        } catch (e: Exception) {
            e.printStackTrace()
            return@withContext VersionResponse(1, false)
        }
    }


    /** 更新token */
    suspend fun updateToken() = withContext(Dispatchers.IO) {
        try {
            val appId = getAppId()
            val appKey = getAppKey()
            val timestamp = getSecondTimestamp()
            val sign = MD5Utils.getMD5String(
                AppUtils.method05(),
                appId,
                appKey,
                timestamp.toString(),
                getExtra()
            )
            val request = CheckAppRequest(appId, timestamp, sign)
            val result = RetrofitUtils.instance.getApi(Api::class.java).checkApp(request)
            if (result == null || TextUtils.isEmpty(result.body) || TextUtils.isEmpty(result.ticket)) {
                return@withContext CheckResponse.getErrorCheckResponse("验证信息返回NULL，验证失败！")
            }
            // 用rsa的私钥去解密ticket，获得aes的秘钥
            val aesKey = EncryptionUtils.rsaDecrypt(result.ticket)
            if (TextUtils.isEmpty(aesKey)) {
                return@withContext CheckResponse.getErrorCheckResponse("Token解密失败，无法继续验证！")
            }
            val jsonBody = EncryptionUtils.aesDecrypt(result.body, aesKey)
            LogC.e("token -> $jsonBody")
            val gson = Gson()
            val checkResponse = gson.fromJson(jsonBody, CheckResponse::class.java)
            if (TextUtils.isEmpty(checkResponse.data)) {
               return@withContext CheckResponse.getErrorCheckResponse("Token返回NULL，验证失败！")
            }
            updateLocalTokenData(checkResponse)
            checkResponse.code = 0
            return@withContext checkResponse
        } catch (e: Exception) {
            e.printStackTrace()
            return@withContext CheckResponse.getErrorCheckResponse("未知异常导致验证失败，请稍后再重试！")
        }
    }

    fun updateLocalTokenData(token: CheckResponse){
        // 保存本地
        val updateTime = System.currentTimeMillis() + token.duration * 1000
        SPUtil.instance.tokenExpireTime = updateTime
        // 保存token
        SPUtil.instance.token = token.data?:""
        // 是否允许定位
        SPUtil.instance.isAllowPosition = token.ap
        SPUtil.instance.videoSourceOrder = token.eo
        SPUtil.instance.tf = token.tf
        if (TextUtils.isEmpty(SPUtil.instance.userId) && !TextUtils.isEmpty(token.uid)) {
            SPUtil.instance.userId = token.uid?:""
        }
    }

    fun <T> doBaseHttp(
        preExec: suspend () -> Any?,
        exc: suspend () -> CommonResponse?,
        callback: OnRetrofitResultListener<T>,
        isCheckToken: Boolean = true
    ): Job? where T : BaseModel {
        if (NetworkUtils.isWifiProxy()) {
            callback.onFailed(AppUtils.getString(R.string.error_not_user_proxy))
            return null
        }
        return viewModelScope.launch {
            try {
                val t = preExec()
                if (t != null) {
                    return@launch
                }

                // 如果token不存在
                if (!isTokenAvailable() && isCheckToken) {
                    val tokenResult = updateToken()
                    if (tokenResult.code != 0) {
                        callback.onFailed(tokenResult.msg?:"验证信息失败，请检查您的网络链接！")
                        return@launch
                    }
                }

                val result = exc()
                if (!isActive) return@launch
                if (result == null) {
                    callback.onFailed(AppUtils.getString(R.string.error_net))
                    return@launch
                }

                if (result.code == 444 && isCheckToken) {
                    LogC.e("doHttp请求过程中出现444，身份验证过期")
                    val tokenResult = updateToken()
                    if (!isActive) return@launch
                    if (tokenResult.code == 0) {
                        LogC.e("刷新token成功。。。重新执行接口")
                        doBaseHttp(exc, callback)
                    } else {
                        callback.onFailed(tokenResult.msg?:"验证信息失败，请检查您的网络链接！")
                    }
                    return@launch
                }

                if (TextUtils.isEmpty(result.ticket) || TextUtils.isEmpty(result.body)) {
                    if (!isActive) return@launch
                    callback.onFailed(AppUtils.getString(R.string.error_net))
                    return@launch
                }

                if (!isActive) return@launch
                // 用rsa的私钥去解密ticket，获得aes的秘钥
                val aesKey = EncryptionUtils.rsaDecrypt(result.ticket)
                if (aesKey == null) {
                    if (!isActive) return@launch
                    callback.onFailed(AppUtils.getString(R.string.error_net))
                    return@launch
                }
                val jsonBody = EncryptionUtils.aesDecrypt(result.body, aesKey)
                LogC.e("jsonBody -> $jsonBody")
                if (jsonBody == null) {
                    if (!isActive) return@launch
                    callback.onFailed(AppUtils.getString(R.string.error_net))
                    return@launch
                }
                //这里获得到的是类的泛型的类型
                if (!isActive) return@launch
                val type = TypeUtils.getInterfaceT(callback, 0)
                if (type != null) {
                    val model = Gson().fromJson<T>(jsonBody, type)
                    if (model?.success == "false") {
                        val msg = model.msg ?: AppUtils.getString(R.string.error_net)
                        callback.onFailed(msg)
                    }
                    callback.onSuccess(model)
                } else {
                    callback.onFailed(AppUtils.getString(R.string.error_net))
                }
            } catch (e: java.lang.Exception) {
                e.printStackTrace()
                if (!isActive) return@launch
                callback.onFailed(AppUtils.getString(R.string.error_net))
            }
        }
    }

    fun <T> doBaseHttp(
        exc: suspend () -> CommonResponse?,
        callback: OnRetrofitResultListener<T>,
        isCheckToken: Boolean = true
    ) where T : BaseModel {
        doBaseHttp({
            withContext(Dispatchers.IO) {
                return@withContext null
            }
        }, exc, callback, isCheckToken)
    }


    open fun isTokenAvailable(): Boolean{
        if (TextUtils.isEmpty(SPUtil.instance.token)) return false
        val createTokenTime = SPUtil.instance.tokenExpireTime
        return  System.currentTimeMillis() < createTokenTime
    }

    fun isNotActive(): Boolean {
        return !viewModelScope.isActive
    }

    fun getCommonRequest(obj: Any): CommonRequest {
        val json = Gson().toJson(obj)
        val commonRequest = CommonRequest()
        commonRequest.body = EncryptionUtils.aesEncrypt(json)
        commonRequest.ticket = EncryptionUtils.rsaEncrypt()
        return commonRequest
    }



    fun <T> doBaseHttp(
        preExec: suspend () -> Any?,
        exc: suspend () -> T?,
        success: (T) -> Unit,
        fail: (errorMsg: String) -> Unit
    ): Job? where T : BaseModel {
        if (NetworkUtils.isWifiProxy()) {
            fail(AppUtils.getString(R.string.error_not_user_proxy))
            return null
        }
        return viewModelScope.launch {
            try {
                val t = preExec()
                if (t != null) {
                    return@launch
                }
                val result = exc()
                if (!isActive) return@launch
                if (result == null) {
                    fail(result?.msg ?: AppUtils.getString(R.string.error_net))
                } else {
                    success(result)
                }
            } catch (e: java.lang.Exception) {
                e.printStackTrace()
                if (!isActive) return@launch
                fail(AppUtils.getString(R.string.error_net))
            }
        }
    }

    fun <T> doBaseHttp(
        exc: suspend () -> T?,
        success: (T) -> Unit,
        fail: (errorMsg: String) -> Unit
    ) where T : BaseModel {
        doBaseHttp({
            withContext(Dispatchers.IO) {
                return@withContext null
            }
        }, exc, success, fail)
    }



    fun <T> doAsync(exe: suspend () -> T?, success: (t: T) -> Unit, fail: () -> Unit) {
        viewModelScope.launch {
            try {
                val result = withContext(Dispatchers.IO) {
                    exe()
                }
                if (isNotActive()) return@launch
                if (result == null) {
                    fail()
                } else {
                    success(result)
                }
            } catch (e: Exception) {
                e.printStackTrace()
                if (isNotActive()) return@launch
                fail()
            }
        }
    }


    // 解密剧集信息
    protected fun decodePlayInfo(playInfo: String?): ArrayList<PlayInfoModel>? {
        return if (!TextUtils.isEmpty(playInfo)) {
            val playInfoStr: String? = getPlayInfoList(playInfo)
            if (TextUtils.isEmpty(playInfoStr)) {
                null
            } else {
                val listType = object : TypeToken<ArrayList<PlayInfoModel>>() {}.type
                val gson = Gson()
                gson.fromJson(playInfoStr, listType) as ArrayList<PlayInfoModel>
            }
        } else {
            null
        }
    }

    protected fun getPlayInfoList(source: String?): String? {
        var videoInfo: String? = null
        try {
            videoInfo = EncryptionUtils.aesDecrypt(source, AppUtils.method07())
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return videoInfo
    }


}